// Author: Domien Nowicki

#include "SingleGame.h"

using namespace std;

SingleGame::SingleGame(const Map& newMap, const Properties& properties, Highscore& newHighscore): Game(properties), map(&newMap), humanMap(0), humanPlayer(0), clock(0), highscore(&newHighscore)
{
	// Hints and coloring the map is allowed for this game
	SetAllowHints(true);
	SetAllowColorMap(true);
}


SingleGame::~SingleGame()
{
	Clear();
}

void SingleGame::Clear()
{
	// Clear clock
	if (clock)
	{
		delete clock;

		clock = 0;
	}

	// Clear human player and map
	if (humanPlayer)
	{
		delete humanPlayer;

		humanPlayer = 0;
	}

	if (humanMap)
	{
		delete humanMap;

		humanMap = 0;
	}
}

int SingleGame::GetTime() const
{
	// Get the total amount of time this game has been running
	return clock->GetTime();
}

const Highscore& SingleGame::GetHighscore()
{
	return *highscore;
}

void SingleGame::Start()
{
	// Start from a fresh game state
	Clear();

	Output("Starting a new single player game.");

	// Start the game
	Game::Start();

	// Create a new ascending clock
	clock = NewClock(Clock::ASCENDING);
	clock->Start();

	// Create player and its map
	humanMap = NewMap(*map);
	humanPlayer = NewHumanPlayer(humanMap);
}


void SingleGame::Stop()
{
	// Stop game and clock
	Game::Stop();

	clock->Stop();
}

bool SingleGame::GetColorMapRestriction(int *colorCount)
{
	bool result = true;

	// Calculate the minimum possible color, and the maximum selected by properties
	int colormin = max((int)MINCOLOR, (int)humanMap->GetUniqueColorCount());
	int colormax = GetProperties().GetColorCount();

	*colorCount = colormin;

	// If the color can be selected by choice
	if (colormin != colormax)
	{
		// Get choice of user
		*colorCount = GetNumber(colormin, colormax);

		// If cancel was pressed, abort
		if (RESTRICTIONABORT == *colorCount)
		{
			result = false;
		}
	}

	return result;
}

void SingleGame::ColorMap()
{
	Move move;
	int colorCount;
	bool ask = true;

	// Can only color map while the game is active, and also try to get a valid color map restriction
	if (!IsActive() || !GetColorMapRestriction(&colorCount))
		return;

	// Let game know that we are coloring the map
	SetColoringMap(true);

	// Stop the clock because there is no point in keeping it running
	clock->Stop();

	// Restrict the map solver by the selected color count
	humanPlayer->RestrictMapSolver(colorCount);

	// While the map is not completed
	while(IsActive() && !humanMap->IsCompleted())
	{
		// Get and do the move which will lead to solving the map
		humanPlayer->GetMove(&move);
		DoMove(humanPlayer, move);

		if (ask)
		{
			// Show the move we did to the user, and ask if we should continue
			switch(ShowMove(move))
			{
				case COLORABORT:
					// Abort
					Stop();
					break;
				case COLORALL:
					ask = false;
					break;
			}
		}
	}
}



void SingleGame::Hint()
{
	// Can only ask for a hint when game is active
	if (IsActive())
	{
		// Do time penalty for asking a hint
		Output("Asking a hint (time penalty):");
		clock->Increase(TIMEPENALTY);

		Move move;

		// Get and do the hint move for the player
		humanPlayer->GetHintMove(&move);

		DoMove(humanPlayer, move);
	}
}

void SingleGame::EndGame(const Player *player)
{
	// The game has ended
	Game::EndGame(player);

	// If the game has ended because of coloring the map automatically, don't add the score to the highscores
	if (IsColoringMap())
		return;


	Score score;

	// Fill in the score data
	score.SetStepCount(player->GetStepCount());
	score.SetTime(GetTime());
	score.SetColorCount(player->GetMap()->GetUniqueColorCount());

	std::string name;

	// Test if the score will get inserted
	if (highscore->TestScoreInsert(score))
	{
		// Ask player's name and insert the highscore if he enterred his name
		if (GetName(&name))
		{
			score.SetName(name);

			// Insert score to highscores and save
			highscore->InsertScore(score);
			highscore->Save();

			// Show highscores to the user
			ShowHighscore(GetHighscore());
		}
	}
}



